package connect2congress.client;

import com.google.gwt.core.client.JsArray;
import com.google.gwt.event.dom.client.*;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Timer;

import com.google.gwt.widgetideas.graphics.client.Color;
import com.google.gwt.widgetideas.graphics.client.GWTCanvas;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;

import connect2congress.client.legacy.Point;
import connect2congress.client.TimelineObserver;
import connect2congress.client.properties.C2CDate;

import java.util.ArrayList;


/**
 * Created by IntelliJ IDEA.
 * User: peter
 * Date: Feb 26, 2010
 * Time: 11:01:56 AM
 * To change this template use File | Settings | File Templates.
 */
public class Timeline extends GWTCanvas implements HasMouseDownHandlers, HasMouseMoveHandlers, HasMouseUpHandlers, Renderable{

    private JsArray<C2CDate> dates;

    private TickList veticks;
    private TickList voticks;

    private double tickDrawWidth = 65;
    private double separatorWidth = 2;

    private long endTime = 1293839999;
    private long startTime = 1230768000;
    
    public ArrayList<TimelineObserver> observers;

    private Connect2Congress parent;
    public Point origin = null;
    public Point opposite = null;
    private static final int LINE_THICKNESS = 2;
    private double indent = Indicator.INDIC_HEIGHT*2;

    private int chamber = Connect2Congress.SENATE;
    private int session = 111;

    private Timer timer;
    private Indicator indicA;
    private Indicator indicB;
    private Indicator pressedIndicator = null;

    private boolean dragging = false;
    private boolean indicSetup = false;

    int voteTickNum1;
    int voteTickNum2;

    int verbTickNum1;
    int verbTickNum2;

    long maxVerb = 0;
    long maxVotes = 0;

    public static final Color TIMELINEBG = new Color(200,200,255);

    public Timeline(Point origin,Point opposite,Connect2Congress parent) {
        super((int)(opposite.x-origin.x),(int)(opposite.y-origin.y));

        this.parent = parent;
        this.origin = origin;
        this.opposite = opposite;
        //System.out.println("origin: " + origin + "\nopposite: " + opposite);
        //this.setSize("1.5em","90%");
        //this.setSize("40px","700px");
        this.setSize(width() + "px",height() + "px");
        //this.setSize("100%","100%");
        super.setBackgroundColor(Timeline.TIMELINEBG);
        //this.setSize(width().toString() + "px",((Double)(opposite.y-origin.y)).toString() + "px");
        //this.setSize("100%","100%");
        veticks = new TickList();
        voticks = new TickList();

        observers = new ArrayList<TimelineObserver>();
        String url = Connect2Congress.TICK_JSON_URL;
        url += "chamber=senate&session=111";
        RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, url + "&type=wnom");


        try {
          Request request = builder.sendRequest(null, new RequestCallback() {
            public void onError(Request request, Throwable exception) {
              System.out.println("Couldn't retrieve JSON");
            }

            public void onResponseReceived(Request request, Response response) {
              if (200 == response.getStatusCode()) {
                System.out.println("Retrieved vote date info for timeline. Setting up timeline!");
                dates = asArrayOfC2CDates(response.getText());

                for(int i = 0; i < dates.length(); i++) {
                    voticks.add(new Tick(dates.get(i),width(), voteDateTickSep(), Connect2Congress.WNOM_TYPE));
                    if(voticks.get(i).getWidth() > maxVotes) {
                        maxVotes = (long) voticks.get(i).getWidth();
                    }
                }
              } else {
                System.out.println("Couldn't retrieve JSON (" + response.getStatusCode()
                    + ")");
              }
            }
          });
        } catch (RequestException e) {
          System.out.println("Couldn't retrieve JSON");
        }

        builder = new RequestBuilder(RequestBuilder.GET, url + "&type=verb");

        try {
          Request request = builder.sendRequest(null, new RequestCallback() {
            public void onError(Request request, Throwable exception) {
              System.out.println("Couldn't retrieve JSON");
            }

            public void onResponseReceived(Request request, Response response) {
              if (200 == response.getStatusCode()) {
                System.out.println("Retrieved vote date info for timeline. Setting up timeline!");
                dates = asArrayOfC2CDates(response.getText());

                for(int i = 0; i < dates.length(); i++) {
                    veticks.add(new Tick(dates.get(i),width(), verbDateTickSep(),Connect2Congress.VERB_TYPE));
                    if(veticks.get(i).getMagnitude() > maxVerb) {
                        maxVerb = (long) veticks.get(i).getMagnitude();
                    }
                }
                setupTicks();

              } else {
                System.out.println("Couldn't retrieve JSON (" + response.getStatusCode()
                    + ")");
              }
            }
          });
        } catch (RequestException e) {
          System.out.println("Couldn't retrieve JSON");
        }

        setupIndicators();

    }

    public void renderingLoop() {
        this.clear();

        if(indicSetup) {
            saveContext();
            drawIndicators();
            this.setFillStyle(new Color(10,10,10));
            this.fillRect(lineMidX()-separatorWidth/2,lineMinY(),separatorWidth,lineHeight());
            restoreContext();
        }
        if(voticks != null && veticks != null) {
            for(int i = 0; i < voticks.size(); i++) {
                if(voticks.get(i) != null) {
                    if(i < voteTickNum1)
                        voticks.get(i).setSelected(false);
                    else if(i > voteTickNum2)
                        voticks.get(i).setSelected(false);
                    else voticks.get(i).setSelected(true);
                }
            }
            for(int i = 0; i < veticks.size(); i++) {
                if(veticks.get(i) != null) {
                    if(i < verbTickNum1)
                        veticks.get(i).setSelected(false);
                    else if(i > verbTickNum2)
                        veticks.get(i).setSelected(false);
                    else veticks.get(i).setSelected(true);
                }
            }
            
            for(int i = 0; i < voticks.size(); i++) {
                if(voticks.get(i) != null) {
                    saveContext();
                    setFillStyle(voticks.get(i).getFillStyle());
                    double width = voticks.get(i).getWidth();
                    width = (double) (width/maxVotes);
                    width = (double) width * maxTickWidth();
                    if(width < 7) {
                        width = 7;
                    }
                    fillRect(lineMidX(),
                            voticks.get(i).
                                    loc.y,   //found!!
                            width,1);

                    restoreContext();
                }
            }
            for(int i = 0; i < veticks.size(); i++) {
                if(veticks.get(i) != null) {
                    saveContext();
                    setFillStyle(veticks.get(i).getFillStyle());
                    double width = veticks.get(i).getWidth();
                    width = (double) (width/maxVerb);
                    width = (double) width * maxTickWidth();
                    fillRect(lineMidX()-(width+separatorWidth),veticks.get(i).loc.y,width,1);

                    restoreContext();
                }
            }

        }
    }



    public void setupTicks() {

        for(int i = 0; i < voticks.size(); i++) {
            voticks.get(i).loc = new Point(lineMinX(),lineMinY() + (lineHeight()*(double)((double)(voticks.get(i).getDate()-startTime)/(double)(endTime - startTime))));
        }
        for(int i = 0; i < veticks.size(); i++) {
            veticks.get(i).loc = new Point(lineMinX(),lineMinY() + (lineHeight()*(double)((double)(veticks.get(i).getDate()-startTime)/(double)(endTime - startTime))));
        }
        indicA.setLoc(new Point(lineMidX()+.5+20,lineMinY()));
        indicB.setLoc(new Point(lineMidX()+.5+20,lineMaxY()));
        indicSetup = true;
        findSelection();
        parent.addToLoop(this);
        
        //ticks.get(ticks.size()-1).loc = new Point(lineMaxX()-Tick.WIDTH,lineMaxY());
    }


    public void indicatorMoved(Point p) {
        findSelection();

        for(int i = 0; i < observers.size(); i++) {
            observers.get(i).loadData(this.voticks.get(voteTickNum1).getDate(),this.voticks.get(voteTickNum2).getDate(),Connect2Congress.WNOM_TYPE);
            observers.get(i).loadData(this.veticks.get(verbTickNum1).getDate(),this.veticks.get(verbTickNum2).getDate(),Connect2Congress.VERB_TYPE);
        }
    }

    private void findSelection() {
        long sd1;
        long sd2;
        sd1 = (long)((double)startTime + (double)(indicA.getLoc().y-lineMinY())/lineHeight() * (double)(endTime-startTime));
        sd2 = (long)((double)startTime + (double)(indicB.getLoc().y-lineMinY())/lineHeight() * (double)(endTime-startTime));

        if(sd2 < sd1) {
            long sd3 = sd1;
            sd1 = sd2;
            sd2 = sd3;
        }
        System.out.println("start: " + sd1 + "\n  end: " + sd2);
        int i = 0;
        while(i < voticks.size()) {
            if(voticks.get(i).getDate() < sd1) {
                voteTickNum1 = i;
            }
            if(voticks.get(i).getDate() < sd2) {
                voteTickNum2 = i;
            }
            i++;
        }
        i = 0;
        while(i < veticks.size()) {
            if(veticks.get(i).getDate() < sd1) {
                verbTickNum1 = i;
            }
            if(veticks.get(i).getDate() < sd2) {
                verbTickNum2 = i;
            }
            i++;
        }
        System.out.println("ticka: "+ verbTickNum1 + " tickb: " + verbTickNum2);

    }

    public double maxTickWidth() {
        return (double) (tickDrawWidth-separatorWidth)/2;
    }

    public double verbDateTickSep() {
        double d = lineHeight()/(veticks.size(Connect2Congress.VERB_TYPE));
        return d;
    }

    public double voteDateTickSep() {
        double d = lineHeight()/(veticks.size(Connect2Congress.WNOM_TYPE));
        //System.out.println("TickSep: " + d);
        return d;
    }

   public long duration() {
        return endTime-startTime;
    }

    public double width() {
        //System.out.println("width: " + this.getStyleElement().getScrollWidth());

        //return this.getOffsetWidth();
        //System.out.println("width: " + (opposite.x-origin.x));
        return opposite.x-origin.x;
    }
    public double height() {
        //System.out.println("height; " + (opposite.y-origin.y));
        return opposite.y-origin.y;
       // System.out.println("height: " + (this.getOffsetHeight()));
        //return this.getOffsetHeight();

    }
    public double lineMinX() {

        return originX() ;
    }
    public double originX() {
       // System.out.println("originx: " + getAbsoluteLeft());
       //return getAbsoluteLeft();
         return origin.x;
    }
    public double originY() {
        return origin.y;
        //System.out.println("originy: " + getAbsoluteTop());
        //return getAbsoluteTop();
    }

    public double lineWidth() {
       return (oppositeX() - originX()) - indent*2;

    }
    public double oppositeX() {
        return originX() + width();
    }
    public double oppositeY() {
        return opposite.y;
    }
    public double lineHeight() {
        //System.out.println("line height: " + (lineMaxY()-lineMinY()));
        return (lineMaxY()-lineMinY());
    }
    public double lineCenterY() {
        double f = origin.y + (opposite.y-origin.y)/2;
        //double f = originY() + (lineMaxY()-lineMinY())/2;
        return f;
    }
    public double lineMinY() {
        //double f = lineCenterY()-(LINE_THICKNESS/2);
        double f = originY() + indent;
        return  f;
    }
    public double lineMaxY() {
        //double f = lineCenterY()+(LINE_THICKNESS/2);
        double f = opposite.y-indent;
        return f;
    }

    public double lineMidX() {
        return (tickDrawWidth-separatorWidth)/2 + originX();
    }
    public double lineMaxX() {
        //return indent + lineWidth() + origin.x;
        return oppositeX();
    }


    public void addObserver(TimelineObserver o) {
        this.observers.add(o);
    }

    public void removeObserver(TimelineObserver o) {
        this.observers.remove(o);
    }

    public void setupIndicators() {
        indicA = new Indicator(this);
        indicB = new Indicator(this);
        indicA.setup();
        indicB.setup();

    }
    public void drawIndicators() {

        indicA.draw();
        indicB.draw();
    }
    public HandlerRegistration addMouseDownHandler(MouseDownHandler handler) {
        return addDomHandler(handler, MouseDownEvent.getType());
    }
    public HandlerRegistration addMouseMoveHandler(MouseMoveHandler handler) {
        return addDomHandler(handler, MouseMoveEvent.getType());
    }
    public HandlerRegistration addMouseUpHandler(MouseUpHandler handler) {
         return addDomHandler(handler, MouseUpEvent.getType());
    }

    //public void mouseMoved(double x, double y) {
        //for(int i = 0; i < ticks.size(); i++) {
           //ticks.get(i).mouseMoved(x,y);
        //}
    //}
    public void mousePressed(double x, double y) {
        dragging = true;
        if(indicA.mousePressed(x,y)) {
            pressedIndicator = indicA;

        } else if(indicB.mousePressed(x,y)) {
            pressedIndicator = indicB;
        }
    }

    public void mouseMoved(double mouseX, double mouseY) {
       if(dragging) {
            if(pressedIndicator != null) {
                //System.out.println("trying to drag indic");

                //System.out.println("trying to drag indic " + mouseY);
                if(mouseY >= lineMinY() && mouseY <= lineMaxY())
                    pressedIndicator.setLoc(new Point(pressedIndicator.getLoc().x,mouseY));

            }
           //findSelection();
           //indicatorMoved(new Point(mouseX,mouseY));

       }
    }
    public void mouseReleased(float x, float y) {
        dragging = false;
        pressedIndicator = null;
        indicA.mouseReleased();
        indicB.mouseReleased();
        findSelection();

    }

    /**
     * Convert the string of JSON into JavaScript object.
     */
     private final native JsArray<C2CDate> asArrayOfC2CDates(String json) /*-{
         return eval(json);
     }-*/;


}
